package org.zoomdev.zoom.dao.builder.driver;

import org.apache.commons.lang3.StringUtils;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.dao.SqlDriver;
import org.zoomdev.zoom.dao.builder.BuilderDriver;
import org.zoomdev.zoom.dao.builder.impl.ColumnBuildInfo;
import org.zoomdev.zoom.dao.builder.impl.TableBuildInfo;
import org.zoomdev.zoom.dao.builder.impl.ZoomDatabaseBuilder;

import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

public class MysqlBuilder implements BuilderDriver {

    private final SqlDriver driver;

    public MysqlBuilder(SqlDriver driver) {
        this.driver = driver;
    }

    protected static void buildUnique(TableBuildInfo table, List<String> sqlList) {
        StringBuilder sb = new StringBuilder();
        //index
        for (ColumnBuildInfo columnMeta : table.getColumns()) {

            if (columnMeta.isUnique()) {
                sb.setLength(0);
                sb.append("ALTER TABLE ")
                        .append(table.getName())
                        .append(" ADD CONSTRAINT UNI_")
                        .append(table.getName().toUpperCase())
                        .append("_")
                        .append(columnMeta.getName().toUpperCase())
                        .append(" UNIQUE ")
                        .append("(")
                        .append(columnMeta.getName())
                        .append(")");
                sqlList.add(sb.toString());
            }
        }
    }

    protected static void buildIndex(TableBuildInfo table, List<String> sqlList) {
        StringBuilder sb = new StringBuilder();
        //index
        for (ColumnBuildInfo columnMeta : table.getColumns()) {

            if (columnMeta.isIndex()) {
                sb.setLength(0);
                sb.append("CREATE INDEX ")
                        .append("IDX_")
                        .append(table.getName())
                        .append("_")
                        .append(columnMeta.getName())
                        .append(" ON ")
                        .append(table.getName())
                        .append("(")
                        .append(columnMeta.getName())
                        .append(")");
                sqlList.add(sb.toString());
            }
        }
    }

    @Override
    public String buildDropIfExists(String table) {
        StringBuilder sb = new StringBuilder();
        sb.append("DROP TABLE IF EXISTS ");
        driver.protect(sb, table);
        return sb.toString();
    }

    protected String protect(String name) {
        StringBuilder sb = new StringBuilder();
        return driver.protect(sb, name).toString();
    }

    protected void buildComment(TableBuildInfo table, List<String> sqlList) {

        if (table.getComment() != null) {
            sqlList.add(String.format("COMMENT ON TABLE %s IS '%s'", protect(table.getName()), table.getComment()));
        }

        for (ColumnBuildInfo columnMeta : table.getColumns()) {
            if (columnMeta.getComment() != null) {
                sqlList.add(String.format("COMMENT ON COLUMN %s.%s IS '%s'",
                        protect(table.getName()),
                        protect(columnMeta.getName()),
                        columnMeta.getComment()));
            }
        }

    }

    @Override
    public void buildTable(TableBuildInfo table, List<String> sqlList) {
        sqlList.add(buildCreateTable(table));
        //index
        buildIndex(table, sqlList);

        buildUnique(table, sqlList);
    }

    protected String buildCreateTable(TableBuildInfo table) {
        List<String> primaryKeys = new ArrayList<String>(3);
        StringBuilder sb = new StringBuilder();

        for (ColumnBuildInfo columnMeta : table.getColumns()) {
            if (columnMeta.isPrimary()) {
                primaryKeys.add(columnMeta.getName());
            }
        }

        sb.append("CREATE TABLE ");
        if (table.isCreateWhenNotExists()) {
            sb.append("IF NOT EXISTS ");
        }
        driver.protect(sb, table.getName());
        sb.append("(\n");
        boolean first = false;
        int index = 0;
        for (ColumnBuildInfo columnMeta : table.getColumns()) {
            sb.append("\t");
            driver.protect(sb, columnMeta.getName());
            sb.append(' ');
            try {
                sb.append(formatColumnType(columnMeta));
            } catch (Exception e) {
                throw new ZoomException("不支持的类型" + columnMeta.getName());
            }


            if (columnMeta.getDefaultValue() != null) {
                if (columnMeta.getDefaultValue() instanceof String) {
                    sb.append(" DEFAULT '").append(columnMeta.getDefaultValue()).append("'");
                } else {
                    if (columnMeta.getDefaultValue() instanceof ZoomDatabaseBuilder.FunctionValue) {
                        sb.append(" DEFAULT ").append(((ZoomDatabaseBuilder.FunctionValue) columnMeta.getDefaultValue()).getValue());
                    } else {
                        sb.append(" DEFAULT ").append(columnMeta.getDefaultValue());
                    }

                }
            } else {
                if (columnMeta.isPrimary()) {
                    if (columnMeta.isAuto()) {
                        sb.append(" PRIMARY KEY");
                        sb.append(" auto_increment".toUpperCase());
                    } else {
                        //single primary key
                        if (primaryKeys.size() == 1) {
                            sb.append(" PRIMARY KEY");
                        }
                    }
                } else {
                    sb.append(columnMeta.isNullable()
                            ? " NULL"
                            : " NOT NULL");
                }
            }

            createColumnComment(sb, columnMeta);

            if (index < table.getColumns().size() - 1) {
                sb.append(",");
            }

            if (index == table.getColumns().size() - 1) {
                break;
            }
            sb.append("\n");
            ++index;
        }

        if (primaryKeys.size() > 1) {
            first = true;

            sb.append(",\n\tPRIMARY KEY (");
            for (String key : primaryKeys) {
                if (first) {
                    first = false;
                } else {
                    sb.append(",");
                }
                sb.append(key);
            }
            sb.append(")\n");

        } else {
            sb.append("\n");
        }

        sb.append(")");

        if (table.engine != null) {
            sb.append(" ENGINE=").append(table.engine);
        }

        sb.append(" charset=utf8");

        createTableComment(sb, table);

        return sb.toString();
    }

    protected void createTableComment(StringBuilder sb, TableBuildInfo table) {
        if (StringUtils.isEmpty(table.getComment())) {
            return;
        }
        sb.append(" COMMENT='").append(table.getComment()).append("'");
    }


    public String formatColumnType(ColumnBuildInfo columnMeta) {
        int type = columnMeta.getType();
        switch (type) {
            case Types.INTEGER:
                if (columnMeta.isUnsigned()) {
                    return "INT UNSIGNED";
                }
                return "INT(11)";
            case Types.SMALLINT:
                if (columnMeta.isUnsigned()) {
                    return "SMALLINT UNSIGNED";
                }
                return "SMALLINT(11)";
            case Types.BIGINT:
                if (columnMeta.isUnsigned()) {
                    return "BIGINT UNSIGNED";
                }
                return "BIGINT(11)";
            case Types.TINYINT:
                if (columnMeta.isUnsigned()) {
                    return "TINYINT UNSIGNED";
                }
                return "TINYINT(1)";
            case Types.VARCHAR:
                return "VARCHAR(" + columnMeta.getMaxLength() + ")";
            case Types.NVARCHAR:
                return "NVARCHAR(" + columnMeta.getMaxLength() + ")";
            case Types.DATE:
                return "DATE";
            case Types.TIME:
                return "TIME";
            case Types.BOOLEAN:
                return "TINYINT(1)";
            case Types.TIMESTAMP:
                return "TIMESTAMP";
            case Types.CHAR:
                return "CHAR(" + columnMeta.getMaxLength() + ")";
            case Types.CLOB:
                if (columnMeta.getMaxLength() == 65535) {
                    return "TEXT";
                }
                return "MEDIUMTEXT";
            case Types.NUMERIC:
            case Types.DOUBLE:
                return "DOUBLE";
            case Types.BLOB:
                if (columnMeta.getSubType() == ColumnBuildInfo.MEDIUM) {
                    return "MEDIUMBLOB";
                }
                return "BLOB";
            case Types.DECIMAL:
                return String.format("DECIMAL(%d,%d)", columnMeta.n, columnMeta.m);

            default:
                throw new ZoomException("不支持的类型" + columnMeta.getType());
        }

    }

    protected void createColumnComment(StringBuilder sb, ColumnBuildInfo columnMeta) {
        if (StringUtils.isEmpty(columnMeta.getComment())) {
            return;
        }
        sb.append(" COMMENT '")
                .append(columnMeta.getComment()).append("'");

    }
}
